package com.novax.ex.activity.task.executor;

import com.alibaba.fastjson.JSON;
import com.novax.ex.activity.infrastructure.entity.BeebUser;
import com.novax.ex.activity.infrastructure.entity.BeebUserFinanceLog;
import com.novax.ex.activity.infrastructure.mapper.BeebUserFinanceLogMapper;
import com.novax.ex.activity.infrastructure.mapper.BeebUserMapper;
import com.novax.ex.activity.task.executor.message.FundsReleaseMessage;
import com.novax.ex.common.constant.MqConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;

@Component
@RequiredArgsConstructor
@Slf4j
public class ScheduledFundReleaseExecutor {

    private final BeebUserMapper beebUserMapper;
    private final BeebUserFinanceLogMapper beebUserFinanceLogMapper;

    private final AmqpTemplate rabbitTemplate;

    private final static Integer LimitPerBatch = 1000;

    public void exec() {
        val total = beebUserMapper.queryBeebUserTotalThatStillNeedFundReleased();
        var released = 0;
        var fromId = 0L;
        while (released < total) {
            val users =  beebUserMapper.queryBeebUserThatStillNeedFundReleasedByCondition(fromId, LimitPerBatch);
            if (CollectionUtils.isEmpty(users)) {
                return;
            }
            users.stream().parallel().filter(this::stillNeedFundReleased).forEach(this::sendFundsReleaseMessageAndLog);

            released += users.size();
            fromId = users.get(users.size() - 1).getId();
        }
    }

    private boolean stillNeedFundReleased(BeebUser beebUser) {
        if (beebUser.getSendTotal().compareTo(beebUser.getUnlockTotal()) <= 0) {
            log.info("userId:{}, id:{}, sendTotal:{}, unlockTotal:{}", beebUser.getUserId(), beebUser.getId(), beebUser.getSendTotal(), beebUser.getUnlockTotal());
            new Thread(() -> {
                log.info("markBeebUserAsNoLongerNeedFundReleased, userId:{}, id:{}", beebUser.getUserId(), beebUser.getId());
                beebUserMapper.markBeebUserAsNoLongerNeedFundReleased(beebUser.getId());
            }).start();
            return false;
        }
        return true;
    }

    private void sendFundsReleaseMessageAndLog(BeebUser beebUser) {
        var unlockAmountThisBatch = beebUser.getUnlockBatchTotal().setScale(8, RoundingMode.DOWN);
        if (beebUser.getUnlockBatch() <= beebUser.getUnlockSendBatch()) {
            unlockAmountThisBatch = beebUser.getSendTotal().subtract(beebUser.getUnlockTotal()).setScale(8, RoundingMode.DOWN);
        }
        if (unlockAmountThisBatch.compareTo(BigDecimal.ZERO) <= 0) {
            log.error("beebUser:{}, unlockAmountThisBatch:{}", beebUser, unlockAmountThisBatch);
        }

        sendFundsReleaseMessage(beebUser, unlockAmountThisBatch);
        logFundsReleaseRecord(beebUser, unlockAmountThisBatch);

        if (beebUser.getUnlockTotal().add(unlockAmountThisBatch).compareTo(beebUser.getSendTotal()) >= 0) {
            new Thread(() -> {
                log.info("markBeebUserAsNoLongerNeedFundReleased, userId:{}, id:{}", beebUser.getUserId(), beebUser.getId());
                beebUserMapper.markBeebUserAsNoLongerNeedFundReleased(beebUser.getId());
            }).start();
        }
    }

    private void sendFundsReleaseMessage(BeebUser user, BigDecimal unlockAmountThisBatch) {
        rabbitTemplate.convertAndSend(MqConstants.Queue.SEND_BEEB_ASSETS, JSON.toJSONString(FundsReleaseMessage.Builder().buildWith(user, unlockAmountThisBatch)));
    }

    private void logFundsReleaseRecord(BeebUser beebUser, BigDecimal unlockAmountThisBatch) {
        new Thread(() -> {
            log.info("updateHasUnlockedBatch, userId:{}, id:{}", beebUser.getUserId(), beebUser.getId());
            beebUserMapper.updateHasUnlockedBatch(beebUser.getId(), unlockAmountThisBatch);

            val date = new Date();
            val financialLog = new BeebUserFinanceLog();
            financialLog.setBeebUserId(beebUser.getId());
            financialLog.setUserId(beebUser.getUserId());
            financialLog.setUnlockTotal(unlockAmountThisBatch);
            financialLog.setUnlockSendBatch(beebUser.getUnlockSendBatch() + 1);
            financialLog.setCreateTime(date);
            financialLog.setUpdateTime(date);
            beebUserFinanceLogMapper.insert(financialLog);
        }).start();
    }
}
